#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include "funciones.h"
#include <errno.h>
#include <limits.h>
#include <stdint.h>

void mostrarVersion()
{
    printf("tp1 (66.20 Organizacion de Computadoras)\n");
    printf("Copyright © 2011\n");
    printf("Versión 1.0\n");
}

void mostrarAyuda ()
{
    printf("Usage:\n");
    printf("common -h\n");
    printf("common -V\n");
    printf("common [options] M N\n");
    printf("Options:\n");
    printf("  -h, --help         Prints usage information.\n");
    printf("  -V, --version      Prints version information.\n");
    printf("  -o, --output       Path to output file.\n");
    printf("  -d, --divisor      Just the divisor.\n");
    printf("  -m, --multiple     Just the multiple.\n");
    printf("Examples:\n");
    printf("common -o - 256 192\n");
}

unsigned int obtenerMaxInt(){
	return INT_MAX;
}


void validarArgumentos(unsigned int m, unsigned int n){
	unsigned int max = obtenerMaxInt();
	unsigned int min = 2;
	int error = 0;

	if (m > max){
		fprintf(stderr, "tp1: El valor de M debe ser un numero que no supere %d\n", max);
		error = 1;
	}
	if (m < min){
		fprintf(stderr, "tp1: El valor de M debe ser un numero mayor que %d\n", min);
		error = 1;
	}
	if (n > max){
		fprintf(stderr, "tp1: El valor de N debe ser un numero que no supere %d\n", max);
		error = 1;
	}
	if (n < min){
		fprintf(stderr, "tp1: El valor de N debe ser un numero mayor que %d\n", min);
		error = 1;
	}
	if (error == 1){
		exit(1);
	}
}

/***********************************************************************************************************/
int main (int argc, char* argv[]){

	const char* const opcionesCortas = "Vho:dm";

	const struct option opcionesLargas[] =
	{
		{"version",    0, NULL, 'V'},
		{"help",       0, NULL, 'h'},
		{"output",     1, NULL, 'o'},
		{"divisor",    1, NULL, 'd'},
		{"multiple",   1, NULL, 'm'},
		{NULL,         0, NULL, 0}
	};

	char* archivoSalida;

	FILE* fpArchivoSalida = stdout;

	unsigned int m = 0;
	unsigned int n = 0;
	uint64_t numeradorMcm = 0;
	long lm = 0;
	long ln = 0;
	int badM = 0;
	int badN = 0;

	int calcularMCM = 1;
	int calcularMCD = 1;
	int mostrarAmbos = 0;

	int siguienteOpcion = 0;
	int cerrarArchivoSalida = 0;

	/*El siguiente bucle inicializa las variables de acuerdo a las opciones enviadas por consola*/
	while (siguienteOpcion != -1){

		siguienteOpcion = getopt_long(argc, argv, opcionesCortas, opcionesLargas, NULL);

		switch (siguienteOpcion){

		case 'h':
			mostrarAyuda();
			exit(0);

		case 'V':
			mostrarVersion();
			exit(0);

		case 'o':
			archivoSalida = optarg;
			char* salidaStandard = "-";
			if(strcmp(optarg,salidaStandard)!=0)
				fpArchivoSalida = fopen(archivoSalida,"w");
			if (fpArchivoSalida == NULL){
				fprintf(stderr, "tp1: No se puede crear o abrir el archivo de salida: %s\n", archivoSalida);
				exit (1);
			}
			cerrarArchivoSalida = 1;
			break;

		case 'm':
			calcularMCD = 0;
			break;

		case 'd':
			calcularMCM = 0;
			break;
		}
	}

	/*Toma de parametros del programa*/
	int cantidadParametros = argc - optind; /*Contiene la cantidad de parametros que se enviaron*/

	if (cantidadParametros !=2){
		fprintf(stderr, "tp1: Cantidad incorrecta de parametros: %d. Deberian ser 2.\n", cantidadParametros);
		exit(1);
	}else{
		char* strM = argv[optind];
		char* strN = argv[optind+1];
		errno = 0;
		lm = strtol(strM, NULL, 10);

	    if ((errno == ERANGE && (lm == LONG_MAX || lm == LONG_MIN)) || (errno != 0 && lm == 0)) {
	    	badM = 1;
		}

	    errno = 0;
	    ln = strtol(strN, NULL, 10);

	    if ((errno == ERANGE && (ln == LONG_MAX || ln == LONG_MIN)) || (errno != 0 && ln == 0)) {
	    	badN = 1;
		}

	    if (badM == 1 || badN == 1){
	    	if (badM == 1){
	    		fprintf(stderr, "tp1: El valor de M se encuentra fuera de rango\n");
	    	}
	    	if (badN == 1){
	    		fprintf(stderr, "tp1: El valor de N se encuentra fuera de rango\n");
	    	}
	    	exit(1);
	    }
	    m = lm;
		n = ln;
	}

	validarArgumentos(m, n);

	if (calcularMCD == 0 && calcularMCM == 0){
		mostrarAmbos = 1;
	}

	if(calcularMCD || mostrarAmbos){
		unsigned int div = mcd(m, n);
		fprintf(fpArchivoSalida, "%d\n", div);
	}

	if(calcularMCM || mostrarAmbos){
		numeradorMcm = (unsigned long long) m * n;

		if (numeradorMcm > obtenerMaxInt()){
			fprintf(stderr, "tp1: Ha ocurrido un error al calcular el mcm debido a un desborde. El producto entre m y n debe ser menor o igual que %d\n", obtenerMaxInt());
			if (cerrarArchivoSalida == 1){
				fclose(fpArchivoSalida);
				fpArchivoSalida = NULL;
			}
			exit(1);
		}

		unsigned int mul = mcm(m, n);
		fprintf(fpArchivoSalida, "%d\n", mul);
	}

	if (cerrarArchivoSalida == 1){
		fclose(fpArchivoSalida);
		fpArchivoSalida = NULL;
	}

	return 0;
}
